﻿using System.Threading;
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace RedisDotNet.Tests
{
    /// <summary>
    /// Operation unit tests
    /// </summary>
    /// <remarks>
    /// NOTE: Please refer to TestBase for how to construct and dispose of
    /// a client instance
    /// </remarks>
    [TestClass]
    public class TestOperations : TestBase
    {
        [TestMethod]
        public void TestGetSetOperation()
        {
            const string data = "All your base are belong to us";
            Assert.IsTrue(redisClient.Set(Key, data));
            Assert.AreEqual(redisClient.Get(Key), data);
        }

        [TestMethod]
        public void TestDeleteOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "TEST"));
            Assert.IsTrue(redisClient.Set(Key2, "TEST"));
            Assert.AreEqual(redisClient.Delete(Key, Key2), 2);
            Assert.IsNull(redisClient.Get(Key));
            Assert.IsNull(redisClient.Get(Key));
        }

        [TestMethod]
        public void TestExistsOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "TEST"));
            Assert.IsTrue(redisClient.Exists(Key));
            Assert.AreEqual(redisClient.Delete(Key), 1);
            Assert.IsFalse(redisClient.Exists(Key));
        }

        [TestMethod]
        public void TestTypeOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "TEST"));
            Assert.AreEqual(redisClient.Type(Key), RedisType.String);
            Assert.AreEqual(redisClient.Delete(Key), 1);
            Assert.AreEqual(redisClient.Type(Key), RedisType.None);
        }

        [TestMethod]
        public void TestIncrOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "1"));
            Assert.AreEqual(redisClient.Incr(Key), 2);
            Assert.AreEqual(redisClient.Get(Key), "2");
        }

        [TestMethod]
        public void TestIncrByOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "1"));
            Assert.AreEqual(redisClient.Incr(Key, 5), 6);
            Assert.AreEqual(redisClient.Get(Key), "6");
        }

        [TestMethod]
        public void TestDecrOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "2"));
            Assert.AreEqual(redisClient.Decr(Key), 1);
            Assert.AreEqual(redisClient.Get(Key), "1");
        }

        [TestMethod]
        public void TestDecrByOperation()
        {
            Assert.IsTrue(redisClient.Set(Key, "6"));
            Assert.AreEqual(redisClient.Decr(Key, 5), 1);
            Assert.AreEqual(redisClient.Get(Key), "1");
        }

        [TestMethod]
        public void TestSaveOperation()
        {
            Assert.IsTrue(redisClient.Save());
        }

        [TestMethod]
        public void TestRenameBasic()
        {
            Assert.IsTrue(redisClient.Set(Key, "RenameValue"));
            Assert.IsTrue(redisClient.Rename(Key, Key2));
            Assert.IsFalse(redisClient.Exists(Key));
            Assert.AreEqual(redisClient.Get(Key2), "RenameValue");
            Assert.AreEqual(redisClient.Delete(Key2), 1);
        }

        [TestMethod]
        public void TestRenameFailFalse()
        {
            Assert.IsTrue(redisClient.Set(Key, "RenameValue"));
            Assert.IsTrue(redisClient.Rename(Key, Key2, false));
            Assert.IsFalse(redisClient.Exists(Key));
            Assert.AreEqual(redisClient.Get(Key2), "RenameValue");
            Assert.AreEqual(redisClient.Delete(Key2), 1);            
        }

        [TestMethod]
        public void TestRenameFailTrueSuccess()
        {
            Assert.IsTrue(redisClient.Set(Key, "RenameValue"));
            Assert.IsTrue(redisClient.Rename(Key, Key2, true));
            Assert.IsFalse(redisClient.Exists(Key));
            Assert.AreEqual(redisClient.Get(Key2), "RenameValue");
            Assert.AreEqual(redisClient.Delete(Key2), 1);
        }

        [TestMethod]
        public void TestRenameFailTrueFailure()
        {
            Assert.IsTrue(redisClient.Set(Key, "V1"));
            Assert.IsTrue(redisClient.Set(Key2, "V2"));
            Assert.IsFalse(redisClient.Rename(Key, Key2, true));
            Assert.AreEqual(redisClient.Get(Key2), "V2");
        }

        [TestMethod]
        public void TestFlushDb()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.AreEqual(redisClient.DbSize(), 1);
            Assert.IsTrue(redisClient.Select(1));
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.AreEqual(redisClient.DbSize(), 1);
            Assert.IsTrue(redisClient.FlushDb());
            Assert.AreEqual(redisClient.DbSize(), 0);
            Assert.IsTrue(redisClient.Select(0));
            Assert.AreEqual(redisClient.DbSize(), 1);
            Assert.IsTrue(redisClient.Exists(Key));
        }

        [TestMethod]
        public void TestDbSizeAndFlushAll()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.AreEqual(redisClient.DbSize(), 1);
            Assert.IsTrue(redisClient.FlushAll());
            Assert.AreEqual(redisClient.DbSize(), 0);
        }

        [TestMethod]
        public void TestTimeToLiveAndExpire()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.AreEqual(redisClient.TimeToLive(Key), -1);
            Assert.IsTrue(redisClient.Expire(Key, 2));
            Thread.Sleep(4000);
            Assert.IsFalse(redisClient.Exists(Key));
        }

        [TestMethod]
        public void TestTimeToLiveAndExpireAt()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.AreEqual(redisClient.TimeToLive(Key), -1);
            Assert.IsTrue(redisClient.Expire(Key, DateTime.Now.AddSeconds(3)));
            Thread.Sleep(5000);
            Assert.IsFalse(redisClient.Exists(Key));
        }

        [TestMethod]
        public void TestRandomKey()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.AreEqual(redisClient.RandomKey(), Key);
        }

        [TestMethod]
        public void TestKeys()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.IsTrue(redisClient.Set(Key2, "Value"));
            var keys = redisClient.Keys("TEST*");
            Assert.AreEqual(keys.Length, 2);
            Assert.IsTrue(keys.Contains(Key));
            Assert.IsTrue(keys.Contains(Key2));
        }

        [TestMethod]
        public void TestSelect()
        {
            Assert.IsTrue(redisClient.Set(Key, "DB0"));
            Assert.IsTrue(redisClient.Select(1));
            Assert.IsTrue(redisClient.Set(Key, "DB1"));
            Assert.IsTrue(redisClient.Select(0));
            Assert.AreEqual(redisClient.Get(Key), "DB0");
            Assert.IsTrue(redisClient.Select(1));
            Assert.AreEqual(redisClient.Get(Key), "DB1");
        }

        [TestMethod]
        public void TestMove()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value"));
            Assert.IsTrue(redisClient.Move(Key, 1));
            Assert.IsFalse(redisClient.Exists(Key));
            Assert.IsTrue(redisClient.Select(1));
            Assert.IsTrue(redisClient.Exists(Key));
        }

        [TestMethod]
        public void TestGetMultiple()
        {
            Assert.IsTrue(redisClient.Set(Key, "Value1"));
            Assert.IsTrue(redisClient.Set(Key2, "Value2"));
            var result = redisClient.Get(new string[] { Key, Key2 });
            Assert.AreEqual(result.Length, 2);
            Assert.AreEqual(result[0], "Value1");
            Assert.AreEqual(result[1], "Value2");
        }
    }
}
